/**
 * The board class
 * @author Minh-Tuan
 *
 */

import java.awt.*;
import java.util.ArrayList;

public class Board {
	//number of dots in height
	private int height;
	
	public int getHeight()
	{
		return height;
	}

	//number of dots in width
	private int width;
	
	public int getWidth()
	{
		return width;
	}
	
	//2 dimensional array of boxes
	Box[][] boxes;
	
	public Box[][] getBoxes()
	{
		return boxes;
	}
	
	public Board(int height, int width)
	{
		this.height = height;
		this.width = width;
		
		//calculate the number of rows and columns
		int rows = height - 1;
		int columns = width - 1;
		
		//create the array of boxes
		boxes = new Box[rows][columns];
		
		//initalize the boxes
		for (int i = 0; i < boxes.length; i++)
		{
			for (int j = 0; j < boxes[i].length; j++)
			{
				boxes[i][j] = new Box(i, j, this);
			}
		}
		
		//call the init methods on each box
		for (int i = 0; i < boxes.length; i++)
		{
			for (int j = 0; j < boxes[i].length; j++)
			{
				boxes[i][j].init();
			}
		}
		
		//everything is set now
	}
	
	//Copy a board
	public Board(Board b)
	{
		this.height = b.height;
		this.width = b.width;
		
		boxes = new Box[height - 1][width - 1];
		
		//initalize the boxes
		for (int i = 0; i < boxes.length; i++)
		{
			for (int j = 0; j < boxes[i].length; j++)
			{
				boxes[i][j] = new Box(b.boxes[i][j], this);
			}
		}
		
		//call the init methods on each box
		for (int i = 0; i < boxes.length; i++)
		{
			for (int j = 0; j < boxes[i].length; j++)
			{
				boxes[i][j].init();
			}
		}
	}
	
	/**
	 * draw the board by drawing each box
	 * @param g
	 */
	public void draw(Graphics g)
	{
		for (int i = 0; i < boxes.length; i++)
		{
			for (int j = 0; j < boxes[i].length; j++)
			{
				boxes[i][j].draw(g);
			}
		}
	}
	
	/**
	 * Evaluate the board
	 */
	public int evaluate(char player)
	{
		int v = 0;
		
		//if wins the game, add +inf to the value. This is the most desirable state
		if (this.winner() == -1)
		{
			v += Integer.MAX_VALUE;
		}
		else
		{
			//each box owns give 5 points
			for (int i = 0; i < boxes.length; i++)
			{
				for (int j = 0; j < boxes[i].length; j++)
				{
					//if the player owns the box, give 10 points
					if (boxes[i][j].getOwner() == 'C')
					{
						v += 10;
					}
					else
					{
						//count the edges in the box
						int edgeCount = 0;
		
						if (boxes[i][j].north)
							edgeCount++;
						if (boxes[i][j].south)
							edgeCount++;
						if (boxes[i][j].west)
							edgeCount++;
						if (boxes[i][j].east)
							edgeCount++;
	
						if (player == 'C')
						{
							//if made a possible win for the player, punish the AI
							if (edgeCount == 3)
							{
								v -= 5;
							}
							else if (edgeCount == 2)
							{
								v += 1;
							}
							else if (edgeCount == 1)
							{
								v += 1;
							}
						}
						else
						{
							//if the AI made the player has to make a move that gives the AI advantage, give some points to the AI
							if (edgeCount == 3)
							{
								v += 5;
							}
							else if (edgeCount == 2)
							{
								v += 1;
							}
							else if (edgeCount == 1)
							{
								v += 1;
							}
						}
					}
				}
			}
		}

		return v;
	}
	
	/**
	 * Player's or computer's move
	 * @param firstDot: the number represent the first dot
	 * @param secondDot: the number represent the second dot
	 * @param player: the character represent the player or computer (p for player, c for computer)
	 * @return 0: move made ok, 1: won a square, -1: can't move
	 */
	public int move(int firstDot, int secondDot, char player)
	{
		int result = -1;
		
		//try to set each box
		for (int i = 0; i < boxes.length; i++)
		{
			for (int j = 0; j < boxes[i].length; j++)
			{
				result = boxes[i][j].move(firstDot, secondDot, player);
				
				//if can set the edge, break out of the loop
				if (result == 0 || result == 1)
					break;
			}
			
			//if can set the edge, break out of the loop
			if (result == 0 || result == 1)
				break;
		}
		
		return result;
	}
	
	/**
	 * redo the move indicate by the dots
	 * @param firstDot: the number represent the first dot
	 * @param secondDot: the number represent the second dot
	 * @param player: the character represent the player or computer (p for player, c for computer)
	 * @return 0: remove ok, not 0: can't remove the move
	 */
	public void redo(int firstDot, int secondDot, char player)
	{
		boolean r = false;
		
		//try to remove the move on each box
		for (int i = 0; i < boxes.length; i++)
		{
			for (int j = 0; j < boxes[i].length; j++)
			{
				r = boxes[i][j].redo(firstDot, secondDot, player);
				
				if (r)
					break;
			}
			
			if (r)
				break;
		}
	}
	
	/**
	 * Determine the winner
	 * @return 1: player wins, -1 computer wins, 0 not finish yet\n -2 draw
	 */
	public int winner()
	{
		int p = 0;
		int c = 0;
		boolean done = true;
		int result = 0;
		
		for (int i = 0; i < boxes.length; i++)
		{
			for (int j = 0; j < boxes[i].length; j++)
			{
				if (boxes[i][j].getOwner() == 'N')
				{
					done = false;
					break;
				}
				else if (boxes[i][j].getOwner() == 'P' || boxes[i][j].getOwner() == 'p')
					p++;
				else
					c++;
			}
			
			if (done == false)
				break;
		}
		
		if (done == false)
			result = 0;
		else if (p > c)
			result = 1;
		else if (c > p)
			result = -1;
		else
			result = -2;

		return result;
	}
	
	/**
	 * Get the available moves of the board
	 * @return array of available moves
	 */
	public ArrayList<Move> getAvailableMoves()
	{
		ArrayList<Move> moves = new ArrayList<Move>();
		
		for (int i = 0; i < boxes.length; i++)
		{
			for (int j = 0; j < boxes[i].length; j++)
			{
				//north available
				if (boxes[i][j].north == false)
				{
					Move t = new Move(boxes[i][j].firstDot, boxes[i][j].secondDot, 0);
					boolean exist = false;
					
					for (int k = 0; k < moves.size(); k++)
					{
						if ((moves.get(k).fDot == t.fDot && moves.get(k).sDot == t.sDot) || (moves.get(k).fDot == t.sDot && moves.get(k).sDot == t.fDot))
						{
							exist = true;
							break;
						}
					}
					
					if (exist == false)
						moves.add(t);
				}
				
				//east available
				if (boxes[i][j].east == false)
				{
					Move t = new Move(boxes[i][j].secondDot, boxes[i][j].thirdDot, 0);
					
					boolean exist = false;
					
					for (int k = 0; k < moves.size(); k++)
					{
						if ((moves.get(k).fDot == t.fDot && moves.get(k).sDot == t.sDot) || (moves.get(k).fDot == t.sDot && moves.get(k).sDot == t.fDot))
						{
							exist = true;
							break;
						}
					}
					
					if (exist == false)
						moves.add(t);
				}
				
				//south available
				if (boxes[i][j].south == false)
				{
					Move t = new Move(boxes[i][j].thirdDot, boxes[i][j].fourthDot, 0);
					
					boolean exist = false;
					
					for (int k = 0; k < moves.size(); k++)
					{
						if ((moves.get(k).fDot == t.fDot && moves.get(k).sDot == t.sDot) || (moves.get(k).fDot == t.sDot && moves.get(k).sDot == t.fDot))
						{
							exist = true;
							break;
						}
					}
					
					if (exist == false)
						moves.add(t);
				}
				
				//west available
				if (boxes[i][j].west == false)
				{
					Move t = new Move(boxes[i][j].fourthDot, boxes[i][j].firstDot, 0);
					
					boolean exist = false;
					
					for (int k = 0; k < moves.size(); k++)
					{
						if ((moves.get(k).fDot == t.fDot && moves.get(k).sDot == t.sDot) || (moves.get(k).fDot == t.sDot && moves.get(k).sDot == t.fDot))
						{
							exist = true;
							break;
						}
					}
					
					if (exist == false)
						moves.add(t);
				}
			}
		}
		
		return moves;
	}
}
